package com.whale.web.json.tree;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.whale.kernel.model.tree.Nodal;
import com.whale.kernel.service.TreeService;
import com.whale.kernel.utils.TreeUtils;

public class TreeJsonProcessor<T extends Nodal<T>> {

	private final TreeService<T> treeService;

	/**
	 * Ограничение уровня загрузки узлов
	 */
	private final static int MAX_LEVEL = 4;

	public TreeJsonProcessor(TreeService<T> treeService) {
		this.treeService = treeService;
	}

	private JSONObject getMenuNodeJSON(T node, Map<Long, T> allNodes,
			int maxLevel) throws JSONException {
		JSONObject menuNode = new JSONObject();
		menuNode.put("title", node.getNodeName());
		menuNode.put("nodeKey", node.getNodeKey());
		if (node.getLocalNodeLevel() >= maxLevel) {
			menuNode.put("fakeNode", true);
			return menuNode;
		}

		List<T> childNodes = TreeUtils.getChildNodes(node.getId(), allNodes);
		if (node.getLocalNodeLevel() == maxLevel - 1 && !childNodes.isEmpty()) {
			// флаг - данную ветку нужно ли под-грузить с сервера
			menuNode.put("requireLoading", true);
		}

		menuNode.put("subnodes",
				getChildMenusNodeJSON(childNodes, allNodes, maxLevel));
		return menuNode;
	}

	private JSONArray getChildMenusNodeJSON(List<T> childNodes,
			Map<Long, T> allNodes, int maxLevel) throws JSONException {
		JSONArray subnodes = new JSONArray();
		for (T child : childNodes) {
			subnodes.put(getMenuNodeJSON(child, allNodes, maxLevel));
		}
		return subnodes;
	}

	/**
	 * Алгоритм для включения узлов с минимальным уровнем т.к. в результате
	 * может быть информация от нескольких родительских узлов одновременно и для
	 * узла будет запоминаться его минимальный уровень вложенности. Это позволит
	 * правильно сделать вывод о необходимости подгрузки узла с сервера
	 * 
	 * @param list
	 * @return
	 */
	private Map<Long, T> normalizeTreeResult(List<T> list) {
		Map<Long, T> nodeMap = new HashMap<Long, T>();
		for (T node : list) {
			Integer level = node.getLocalNodeLevel();
			Long nodeId = node.getId();
			if (nodeMap.containsKey(nodeId)) {
				T exNode = nodeMap.get(nodeId);
				if (exNode.getLocalNodeLevel() < level) {
					nodeMap.put(nodeId, exNode);
				}
			} else {
				nodeMap.put(nodeId, node);
			}
		}
		return nodeMap;
	}

	public JSONObject feedRootNodes() throws JSONException {
		// MENU_MAX_LEVEL + 1: означает что из базы достаем на один уровень
		// больше, для того что бы определить необходимость подгружать дочерние
		// узлы
		List<T> allNodes = treeService.getTreeRootNodes(MAX_LEVEL + 1);
		Map<Long, T> allNodesMap = normalizeTreeResult(allNodes);

		List<T> rootNodes = TreeUtils.getRootNodes(allNodesMap);
		JSONObject top = new JSONObject();
		top.put("topnodes",
				getChildMenusNodeJSON(rootNodes, allNodesMap, MAX_LEVEL));
		return top;
	}

	public JSONObject feedChildNodes(Long nodeId) throws JSONException {
		List<T> allNodes = treeService.getChildNodes(
				nodeId, MAX_LEVEL+1);
		Map<Long, T> allNodesMap = normalizeTreeResult(allNodes);
		// должен быть только один элемент верхнего уровня
		List<T> rootNodes = TreeUtils.getRootNodes(allNodesMap);
		return getMenuNodeJSON(rootNodes.get(0), allNodesMap, MAX_LEVEL);
	}

	public JSONObject feedTreeScopeNodes(Long nodeId) throws JSONException {
		List<T> allNodes = treeService.getTreeScopeNodes(
				nodeId, MAX_LEVEL);
		Map<Long, T> allNodesMap = normalizeTreeResult(allNodes);
		List<T> rootNodes = TreeUtils.getRootNodes(allNodesMap);
		JSONObject top = new JSONObject();
		top.put("topnodes",
				getChildMenusNodeJSON(rootNodes, allNodesMap, MAX_LEVEL));
		return top;
	}
}
